#!/usr/bin/perl

use FindBin qw($Bin $Script);
use Cwd qw(getcwd abs_path);
$Bin = abs_path($Bin);

use IO::Socket::INET;
use Digest::SHA qw(sha256_hex);
#use Digest::MD5 qw(md5_hex);
use Time::HiRes qw(gettimeofday);

my $debug = 1;

my $unknown = "UNK";
my $host = "127.0.0.1";
my $port = 5000;
my $resetpw = "admin";
my $pub_port = 5911;
my $pub_resetpw = "public";

my @sensorlist = qw();
my @neighborlist = qw();
my $alarm_wincnt = 0;
my $alarm_flag = 0;

while (1) {
	do_monitor();
	sleep(1);
}

exit(0);

########################################################################
sub do_monitor {
	($#sensorlist, $#neighborlist) = readConsoleData(\@sensorlist, \@neighborlist);

	#for $i(0 .. $#sensorlist) {
	#	my $msg = $i;
	#	$msg .= " Name=$sensorlist[$i]{Name}, ";
	#	$msg .= " Type=$sensorlist[$i]{Type}, ";
	#	$msg .= " SetVal=$sensorlist[$i]{SetVal}, ";
	#	my_print("***Sensor List: $msg\n");
	#}
	#for $i(0 .. $#neighborlist) {
	#	my $msg = $i;
	#	$msg .= " Name=$neighborlist[$i]{Name}, ";
	#	$msg .= " Address=$neighborlist[$i]{Address}, ";
	#	my_print("***Neighbor List: $msg\n");
	#}

	my $flag = "OK";

	for $i(0 .. $#sensorlist) {
		my $p = $port+$i;
		if ($sensorlist[$i]{Mode} eq "Enable") {
			my $socket = connectSensorD($host, $p);
			my($rc, $health) = getHealthData($socket, $host, $port+$i, $resetpw);
			$health =~ s/( +)//g;
			if ($rc == 0) {
				$health = $unknown;
			}
			if ($health ne "OK") {
				$flag = "BAD";
			}
		}
	}

	for $i(0 .. $#neighborlist) {
		my $h = $neighborlist[$i]{Address};
		if ($neighborlist[$i]{Mode} eq "Enable") {
			my $socket = connectSensorD($h, $pub_port);
			my($rc, $health) = getHealthData($socket, $h, $pub_port, $pub_resetpw);
			$health =~ s/( +)//g;
			if ($rc == 0) {
				$health = $unknown;
			}
			if ($health ne "OK") {
				$flag = "BAD";
			}
		}
	}

	if ($flag ne "OK") {
		if (($alarm_wincnt <= 0) || ($alarm_flag == 0)) {
			$alarm_flag = 1;
			$alarm_wincnt = 10;

			killProc("$Bin/alarm_on.pl");
			system("/usr/bin/perl $Bin/alarm_on.pl &");
		}
		$alarm_wincnt -= 1;
	}
	else {
		$alarm_flag = 1;
		$alarm_flag = 0;
		killProc("$Bin/alarm_on.pl");
	}
}

########################################################################
#writeConsoleData(\@sensorlist, \@neighborlist, $#sensorlist, $#neighborlist);
########################################################################
sub writeConsoleData {
	my($sensorlist, $neighborlist, $sensorlist_cnt, $neighborlist_cnt) = @_;

	my $ctrl_data = "$Bin/consoledata.udb";
	if (open(FH, ">$ctrl_data")) {
		for $i(0 .. $sensorlist_cnt) {
			print(FH "[Sensor]\n");
			for $role (keys %{$sensorlist->[$i]}){
				print(FH "$role=$sensorlist->[$i]{$role}\n");
			}
		}
		for $i(0 .. $neighborlist_cnt) {
			print(FH "[Neighbor]\n");
			for $role (keys %{$neighborlist->[$i]}){
				print(FH "$role=$neighborlist->[$i]{$role}\n");
			}
		}
		close(FH);
	}
	else {
		$answer = "Unable to save the data to file\n\"$sensor_data\"\n";
		return 0;
	}
	return 1;
}

########################################################################
sub readConsoleData {
	my($sensorlist, $neighborlist) = @_;

	my $ctrl_data = "$Bin/consoledata.udb";
	if (open(FH, $ctrl_data)) {
		my $mode = "";
		my $i = -1;
		my $j = -1;
		while (<FH>) {
			chomp($_);
			$_ =~ s/^( +)//; #remove spaces in front
			if ($_ =~ /\[Sensor/) {
				$i += 1;
				$mode = "Sensor";
			}
			elsif ($_ =~ /\[Neighbor/) {
				$j += 1;
				$mode = "Neighbor";
			}
			else {
				if ($mode eq "Sensor") {
					my($role, $value) = split(/=/, $_);
					$sensorlist->[$i]{$role} = $value;
				}
				elsif ($mode eq "Neighbor") {
					my($role, $value) = split(/=/, $_);
					$neighborlist->[$j]{$role} = $value;
				}
			}
		}
		close(FH);
		return($i, $j);
	}
	else {
		return(-1, -1);
	}
}

########################################################################
sub connectSensorD {
	my($host, $port) = @_;

	my $socket = new IO::Socket::INET (
		PeerHost => $host,
		PeerPort => $port,
		Proto => "tcp",
	);
	if ($socket == 0) {
		my_print("MON: Socket creation error: $!\n");
		return(0);
	};

	$socket->autoflush(1);
	my_print("MON: TCP connection successful: $host port $port\n");
	return($socket);
}

########################################################################
sub getHealthData {
	my($socket, $host, $port, $passwd) = @_;

	if ($socket == 0) {
		return(0, "");
	}

	#Request the nonce
	my_print("MON: Requesting nonce...");
	print $socket "REQ_NONCE\n";

	#Receive the nonce
	my $nonce = undef;
	$socket->recv($nonce, 1024);
	chomp $nonce;

	if ($nonce =~ /^NONCE ([a-f0-9]+)$/) {
		my_print("MON: $nonce\n");
		$nonce = $1;
	}
	else {
		my_print("MON: Problem, the sensor would not provide nonce.\n");
		return(0, "");
	}

	# Build our nonce, encode authentication, then request status.
	my_print("MON: Requesting status... ");
	my $cnonce = join '', gettimeofday();

	#my $auth_hash = md5_hex("REQ_STATUS user".$nonce.$cnonce.$passwd);
	my $auth_hash = sha256_hex("REQ_STATUS user".$nonce.$cnonce.$passwd);

	# Take our selected action - get status
	my_print("MON: REQ_STATUS,user,$cnonce,$auth_hash\n");
	print $socket "REQ_STATUS,user,$cnonce,$auth_hash\n";

	# Receive the response, the actual status message, why we're here, etc.
	my $response = undef;
	$socket->recv($response, 1024);
	chomp $response;
	if ($response =~ /^STATUS/) {
		my($cmd,$nonce,$hash) = split(/,/, $response);

		#my $auth_hash = md5_hex($cmd.$nonce.$cnonce.$passwd);
		my $auth_hash = sha256_hex($cmd.$nonce.$cnonce.$passwd);

		my_print("MON: $response\n");
		my_print("MON: SENSOR $host:$port cmd=$cmd, hash=$hash(expect:$auth_hash)\n");
		if ($auth_hash eq $hash) {
			$cmd =~ s/^STATUS //;
			$cmd =~ s/\(\)//g;
			return(1, $cmd);
		}
	}
	my_print("MON: Problem, invalid response.\n");
	return(0,"");
}

########################################################################
sub killProc {
	my($pname) = @_;

	my @p = `/bin/ps -ef |grep $pname |grep -v grep`;

	foreach my $m (@p) {
		$m =~ s/( +)/ /g;  #combine multiple spaces to one
		my @tok = split(/ /, $m);
		my $rc = `kill -9 $tok[1]`;
	}
}

########################################################################
sub my_print {
	my($msg) = @_;

	if ($debug) {
		print($msg);
	}
}
